/*
 * Copyright 2011 Philippe Blanc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package me.l1k3.fx.client.engine;

import com.google.gwt.core.client.GWT;

import me.l1k3.fx.client.channel.inter.Channel;
import me.l1k3.fx.client.channel.inter.ChannelContext;
import me.l1k3.fx.client.engine.easing.Easing;
import me.l1k3.fx.client.engine.impl.FXEngineJS;
import me.l1k3.fx.client.engine.inter.FXEngine;
import me.l1k3.fx.client.engine.inter.FXEngineCSS3;
import me.l1k3.fx.client.engine.inter.FXEngineConnector;

public class FXHybridEngine extends FXEngineJS {
    public final static String HYBRID = "js-Hybrid";
    
    private final FXEngineConnector connector = new HybridConnector();
    private FXEngine engine;
    private int count;
    
    public FXHybridEngine(FXEngine engine) {
        setFX(engine.getFX());
        
        try {
            setEasing(engine.getEasing());
        }
        catch(Easing.EasingNotSupportedException e) {
            setEasing(0.25, 0.1, 0.25, 1.0); //default to ease
            engine.setEasing(0.25, 0.1, 0.25, 1.0); //default to ease
        }

        if(FXEngine.JS.equals(engine.toString())) {
            engine.unsetFX(getFX());
            
            FXEngineCSS3 engineCC3 = GWT.create(FXEngineCSS3.class);
            
            if(FXEngine.CSS3.equals(engineCC3.toString())) {
                setEngine(engineCC3);
            }
        }
        else {
            setEngine(engine);
        }
    }
    
    @Override
    public void setFX(FXEngineConnector fx) {
        super.setFX(fx);
        if(engine!=null) {
            engine.setFX(getHybridFX());
        }
    }

    @Override
    public void unsetFX(FXEngineConnector fx) {
        super.unsetFX(fx);
        if(engine!=null) {
            engine.unsetFX(getHybridFX());
        }
    }

    @Override
    public void setEasing(String easing) throws Easing.EasingNotSupportedException {
        super.setEasing(easing);
        if(engine!=null) engine.setEasing(easing);
    }

    @Override
    public void setEasing(Easing easing) throws Easing.EasingNotSupportedException {
        super.setEasing(easing);
        if(engine!=null) engine.setEasing(easing);
    }
    
    @Override
    public void setEasing(double x1, double y1, double x2, double y2) {
        super.setEasing(x1, y1, x2, y2);
        if(engine!=null) engine.setEasing(x1, y1, x2, y2);
    }

    @Override
    public boolean validateChannel(Channel channel) {
        super.validateChannel(channel);
        if(engine!=null) engine.validateChannel(channel);
        
        return true; //regardless
    }

    @Override
    public void cancel() {
        super.cancel();
        if(count==2 && engine!=null) { //both engine are running - count == 1 when the css3 engine wait for the js engine
            engine.cancel();
        }
    }
    
    @Override
    protected void onStart() {       
        count();
        super.onStart();
    }
    
    @Override
    protected void onComplete() {       
        uncount();
    }
    
    @Override
    public void run(int duration, double startTime) {
        engine.hookFire(duration);
        super.run(duration, startTime);
    }
    
    //- - -
    
    protected FXEngineConnector getHybridFX() {
        return connector;
    }
    
    protected final void count() {
        count++;      
    }
    
    protected final void uncount() {
        count--;

        if(count==0) {
            super.onComplete();
        }
    }
    
    protected void setEngine(FXEngine engine) {
        this.engine = engine;
        if(engine!=null) {
            engine.setFX(getHybridFX());
           
            try {
                engine.setEasing(getEasing());
            }
            catch(Easing.EasingNotSupportedException e) {
                engine.setEasing(0.25, 0.1, 0.25, 1.0); //default to ease
            }
        }
    }
    
    //- - -
    
    //we are using the javascript implementation to dialog with the real connector
    //we sync both onComplete event with a counter
    private final class HybridConnector implements FXEngineConnector {
        @Override
        public final void onStart() {           
            FXHybridEngine.this.count();
        }

        @Override
        public final void onComplete() {          
            FXHybridEngine.this.uncount();
        }

        @Override
        public final void onUpdate(double progress) { }
        
        @Override
        public final ChannelContext getContext() {
            return FXHybridEngine.this.getContext();
        }
    }
    
    //- - -
    
    @Override
    public String toString() {
        return HYBRID;
    }
}
